<!DOCTYPE html>
<title>NDEFRecord constructor</title>
<link rel="help" href="https://w3c.github.io/web-nfc/#dom-ndefrecord">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/nfc-helpers.js"></script>
<script>

  test(() => {
    assert_equals(NDEFRecord.length, 1);
    assert_throws_js(TypeError, () => new NDEFRecord());
  }, 'NDEFRecord constructor without init dict');

  test(() => {
    assert_throws_js(TypeError, () => new NDEFRecord(null),
        'NDEFRecordInit#recordType is a required field.');
  }, 'NDEFRecord constructor with null init dict');

  test(() => {
    assert_throws_js(TypeError, () => new NDEFRecord({id: test_record_id, data: test_text_data}),
        'NDEFRecordInit#recordType is a required field.');
  }, 'NDEFRecord constructor without NDEFRecordInit#recordType field');

  test(() => {
    assert_throws_js(TypeError, () => new NDEFRecord(
          createRecord('empty', test_text_data, test_record_id)),
        'id does not apply for empty record type.');
  }, 'NDEFRecord constructor with empty record type and id');

  test(() => {
    assert_throws_js(TypeError, () => new NDEFRecord(
          createRecord('empty', test_text_data, test_record_id, 'text/plain')),
        'mediaType does not apply for empty record type.');
    assert_throws_js(TypeError, () => new NDEFRecord(
          createRecord('text', test_text_data, test_record_id, 'text/plain')),
        'mediaType does not apply for text record type.');
    assert_throws_js(TypeError, () => new NDEFRecord(
          createRecord('url', test_url_data, test_record_id, 'text/plain')),
        'mediaType does not apply for url record type.');
    assert_throws_js(TypeError, () => new NDEFRecord(
          createRecord('absolute-url', test_url_data, test_record_id, 'text/plain')),
        'mediaType does not apply for absolute-url record type.');
    assert_throws_js(TypeError, () => new NDEFRecord(
          createRecord('unknown', test_buffer_data, test_record_id, 'application/octet-stream')),
        'mediaType does not apply for unknown record type.');
    assert_throws_js(TypeError, () => new NDEFRecord(
          createRecord('foo.example.com:bar', test_buffer_data, test_record_id, 'application/octet-stream')),
        'mediaType does not apply for external record type.');
  }, 'NDEFRecord constructor should only accept mediaType for mime record type');

  test(() => {
    {
      const record = new NDEFRecord(createRecord('text', test_text_data));
      assert_equals(record.id, null, 'id');
    }
    {
      const record = new NDEFRecord(createRecord('text', test_text_data, ''));
      assert_equals(record.id, '', 'id');
    }
    {
      const dummy_id = 'https://dummy_host/mypath/myid';
      const record = new NDEFRecord(createRecord('text', test_text_data, dummy_id));
      assert_equals(record.id, dummy_id, 'id');
    }
    {
      const dummy_id = 'http://dummy_host/mypath/myid';
      const record = new NDEFRecord(createRecord('text', test_text_data, dummy_id));
      assert_equals(record.id, dummy_id, 'id');
    }
    {
      const dummy_id = 'mypath/myid';
      const record = new NDEFRecord(createRecord('text', test_text_data, dummy_id));
      assert_equals(record.id, dummy_id, 'id');
    }
  }, 'NDEFRecord constructor with custom record ids');

  test(() => {
    const record = new NDEFRecord(createRecord('empty'));
    assert_equals(record.recordType, 'empty', 'recordType');
    assert_equals(record.mediaType, null, 'mediaType');
    assert_equals(record.id, null, 'id');
    assert_equals(record.encoding, null, 'encoding');
    assert_equals(record.lang, null, 'lang');
    assert_equals(record.data, null, 'data');
    assert_throws_dom('NotSupportedError', () => record.toRecords(),
        'Only smart-poster, external type and local type records could have embedded records.');
  }, 'NDEFRecord constructor with empty record type');

  test(() => {
    const record = new NDEFRecord(createTextRecord(test_text_data));
    assert_equals(record.recordType, 'text', 'recordType');
    assert_equals(record.mediaType, null, 'mediaType');
    assert_equals(record.id, test_record_id, 'id');
    assert_equals(record.encoding, 'utf-8', 'encoding');
    assert_equals(record.lang, 'en', 'lang');
    const decoder = new TextDecoder();
    assert_equals(decoder.decode(record.data), test_text_data,
        'data has the same content with the original dictionary');
    assert_throws_dom('NotSupportedError', () => record.toRecords(),
        'Only smart-poster, external type and local type records could have embedded records.');
  }, 'NDEFRecord constructor with text record type and string data');

  test(() => {
    const encoder = new TextEncoder();
    const uint8Array = encoder.encode(test_text_data);
    const record = new NDEFRecord(createTextRecord(uint8Array.buffer));
    assert_equals(record.recordType, 'text', 'recordType');
    assert_equals(record.mediaType, null, 'mediaType');
    assert_equals(record.id, test_record_id, 'id');
    // By default, 'utf-8'.
    assert_equals(record.encoding, 'utf-8', 'encoding');
    assert_equals(record.lang, 'en', 'lang');
    const decoder = new TextDecoder();
    assert_equals(decoder.decode(record.data), test_text_data,
        'data has the same content with the original dictionary');
    assert_throws_dom('NotSupportedError', () => record.toRecords(),
        'Only smart-poster, external type and local type records could have embedded records.');
  }, 'NDEFRecord constructor with text record type and arrayBuffer data');

  test(() => {
    const encoder = new TextEncoder();
    const uint8Array = encoder.encode(test_text_data);
    const record = new NDEFRecord(createTextRecord(uint8Array));
    assert_equals(record.recordType, 'text', 'recordType');
    assert_equals(record.mediaType, null, 'mediaType');
    assert_equals(record.id, test_record_id, 'id');
    // By default, 'utf-8'.
    assert_equals(record.encoding, 'utf-8', 'encoding');
    assert_equals(record.lang, 'en', 'lang');
    const decoder = new TextDecoder();
    assert_equals(decoder.decode(record.data), test_text_data,
        'data has the same content with the original dictionary');
    assert_throws_dom('NotSupportedError', () => record.toRecords(),
        'Only smart-poster, external type and local type records could have embedded records.');
  }, 'NDEFRecord constructor with text record type and arrayBufferView data');

  test(() => {
    assert_throws_js(TypeError, () => new NDEFRecord(createTextRecord(
            test_text_data, 'random-encoding')));
    assert_throws_js(TypeError, () => new NDEFRecord(createTextRecord(
            test_text_data, 'utf-16')));
    // Only 'utf-8' is OK for a DOMString data source.
    const record = new NDEFRecord(createTextRecord(test_text_data, 'utf-8', 'fr'));
    assert_equals(record.recordType, 'text', 'recordType');
    assert_equals(record.encoding, 'utf-8', 'encoding');
    assert_equals(record.lang, 'fr', 'lang');
    const decoder = new TextDecoder();
    assert_equals(decoder.decode(record.data), test_text_data,
        'data has the same content with the original text');

    assert_throws_js(TypeError, () => new NDEFRecord(createTextRecord(
            encodeTextToArrayBuffer(test_text_data, 'utf-8'), 'random-encoding')));
    // The encoding list valid for a BufferSource data source.
    const encodings = ['utf-8', 'utf-16', 'utf-16be', 'utf-16le'];
    for (const encoding of encodings) {
      const record = new NDEFRecord(createTextRecord(encodeTextToArrayBuffer(
              test_text_data, encoding), encoding, 'fr'));
      assert_equals(record.recordType, 'text', 'recordType');
      assert_equals(record.encoding, encoding, 'encoding');
      assert_equals(record.lang, 'fr', 'lang');
      const decoder = new TextDecoder(record.encoding);
      assert_equals(decoder.decode(record.data), test_text_data,
          'data has the same content with the original text. encoding: ' + encoding);
    }
  }, 'NDEFRecord constructor with text record type, encoding, and lang');

  test(t => {
    const previous_lang = document.querySelector('html').getAttribute('lang');
    const test_lang = 'fr';
    document.querySelector('html').setAttribute('lang', test_lang);
    t.add_cleanup(() => {
      document.querySelector('html').setAttribute('lang', previous_lang);
    });
    const record = new NDEFRecord(createTextRecord(test_text_data));
    assert_equals(record.recordType, 'text', 'recordType');
    assert_equals(record.mediaType, null, 'mediaType');
    assert_equals(record.id, test_record_id, 'id');
    assert_equals(record.encoding, 'utf-8', 'encoding');
    assert_equals(record.lang, test_lang, 'lang');
    const decoder = new TextDecoder();
    assert_equals(decoder.decode(record.data), test_text_data,
        'data has the same content with the original dictionary');
  }, 'NDEFRecord constructor with text record type and custom document language');

  test(() => {
    const record = new NDEFRecord(createUrlRecord(test_url_data));
    assert_equals(record.recordType, 'url', 'recordType');
    assert_equals(record.mediaType, null, 'mediaType');
    assert_equals(record.id, test_record_id, 'id');
    const decoder = new TextDecoder();
    assert_equals(decoder.decode(record.data), test_url_data,
        'data has the same content with the original dictionary');
    assert_throws_dom('NotSupportedError', () => record.toRecords(),
        'Only smart-poster, external type and local type records could have embedded records.');
  }, 'NDEFRecord constructor with url record type');

  test(() => {
    const record = new NDEFRecord(createUrlRecord(test_url_data, true));
    assert_equals(record.recordType, 'absolute-url', 'recordType');
    assert_equals(record.mediaType, null, 'mediaType');
    assert_equals(record.id, test_record_id, 'id');
    const decoder = new TextDecoder();
    assert_equals(decoder.decode(record.data), test_url_data,
        'data has the same content with the original dictionary');
    assert_throws_dom('NotSupportedError', () => record.toRecords(),
        'Only smart-poster, external type and local type records could have embedded records.');
  }, 'NDEFRecord constructor with absolute-url record type');

  test(() => {
    assert_throws_js(TypeError, () => new NDEFRecord(
          createMimeRecord("A string is not a BufferSource")),
        'Only BufferSource is allowed to be the record data.');

    let buffer = new ArrayBuffer(4);
    new Uint8Array(buffer).set([1, 2, 3, 4]);
    // Feed ArrayBuffer.
    {
      const record = new NDEFRecord(createMimeRecord(buffer));
      assert_equals(record.recordType, 'mime', 'recordType');
      assert_equals(record.mediaType, 'application/octet-stream', 'mediaType');
      assert_equals(record.id, test_record_id, 'id');
      assert_array_equals(new Uint8Array(record.data.buffer), [1, 2, 3, 4],
          'data has the same content with the original buffer');
      assert_throws_dom('NotSupportedError', () => record.toRecords(),
          'Only smart-poster, external type and local type records could have embedded records.');
    }
    // Feed ArrayBufferView.
    {
      let buffer_view = new Uint8Array(buffer, 1);
      const record = new NDEFRecord(createMimeRecord(buffer_view));
      assert_equals(record.recordType, 'mime', 'recordType');
      assert_equals(record.id, test_record_id, 'id');
      assert_array_equals(new Uint8Array(record.data.buffer), [2, 3, 4],
          'data has the same content with the original buffer view');
      assert_throws_dom('NotSupportedError', () => record.toRecords(),
          'Only smart-poster, external type and local type records could have embedded records.');
    }
  }, 'NDEFRecord constructor with mime record type and stream data');

  test(() => {
    const record = new NDEFRecord(createMimeRecordFromJson(test_json_data));
    assert_equals(record.recordType, 'mime', 'recordType');
    assert_equals(record.mediaType, 'application/json', 'mediaType');
    assert_equals(record.id, test_record_id, 'id');
    assert_object_equals(JSON.parse(new TextDecoder().decode(record.data)),
        test_json_data,
        'data has the same content with the original json');
    assert_throws_dom('NotSupportedError', () => record.toRecords(),
        'Only smart-poster, external type and local type records could have embedded records.');
  }, 'NDEFRecord constructor with mime record type and json data');

  test(() => {
    assert_throws_js(TypeError, () => new NDEFRecord(
          createUnknownRecord("A string is not a BufferSource")),
        'Only BufferSource is allowed to be the record data.');

    let buffer = new ArrayBuffer(4);
    new Uint8Array(buffer).set([1, 2, 3, 4]);
    // Feed ArrayBuffer.
    {
      const record = new NDEFRecord(createUnknownRecord(buffer));
      assert_equals(record.recordType, 'unknown', 'recordType');
      assert_equals(record.id, test_record_id, 'id');
      assert_array_equals(new Uint8Array(record.data.buffer), [1, 2, 3, 4],
          'data has the same content with the original buffer');
      assert_throws_dom('NotSupportedError', () => record.toRecords(),
          'Only smart-poster, external type and local type records could have embedded records.');
    }
    // Feed ArrayBufferView.
    {
      let buffer_view = new Uint8Array(buffer, 1);
      const record = new NDEFRecord(createUnknownRecord(buffer_view));
      assert_equals(record.recordType, 'unknown', 'recordType');
      assert_equals(record.id, test_record_id, 'id');
      assert_array_equals(new Uint8Array(record.data.buffer), [2, 3, 4],
          'data has the same content with the original buffer view');
      assert_throws_dom('NotSupportedError', () => record.toRecords(),
          'Only smart-poster, external type and local type records could have embedded records.');
    }
  }, 'NDEFRecord constructor with unknown record type');

  test(() => {
    assert_throws_js(TypeError, () => new NDEFRecord(
        createRecord('foo.eXamPle.com:bAr*-', "A string is not a BufferSource or NDEFMessageInit")),
        'Only BufferSource and NDEFMessageInit are allowed to be the record data.');

    let buffer = new ArrayBuffer(4);
    new Uint8Array(buffer).set([1, 2, 3, 4]);
    // Feed ArrayBuffer.
    {
      const record = new NDEFRecord(createRecord('foo.eXamPle.com:bAr*-', buffer, test_record_id));
      assert_equals(record.recordType, 'foo.eXamPle.com:bAr*-', 'recordType');
      assert_equals(record.mediaType, null, 'mediaType');
      assert_equals(record.id, test_record_id, 'id');
      assert_array_equals(new Uint8Array(record.data.buffer), [1, 2, 3, 4],
          'data has the same content with the original buffer');
      assert_equals(record.toRecords(), null,
          'toRecords() returns null if the payload is not an NDEF message.');
    }
    // Feed ArrayBufferView.
    {
      let buffer_view = new Uint8Array(buffer, 1);
      const record = new NDEFRecord(createRecord(
            'foo.eXamPle.com:bAr*-', buffer_view, test_record_id));
      assert_equals(record.recordType, 'foo.eXamPle.com:bAr*-', 'recordType');
      assert_equals(record.mediaType, null, 'mediaType');
      assert_equals(record.id, test_record_id, 'id');
      assert_array_equals(new Uint8Array(record.data.buffer), [2, 3, 4],
          'data has the same content with the original buffer view');
      assert_equals(record.toRecords(), null,
          'toRecords() returns null if the payload is not an NDEF message.');
    }
    // Feed NDEFMessageInit.
    {
      const payload_message = createMessage([createTextRecord(test_text_data)]);
      const record = new NDEFRecord(createRecord(
            'foo.eXamPle.com:bAr*-', payload_message, "dummy_record_id"));
      assert_equals(record.recordType, 'foo.eXamPle.com:bAr*-', 'recordType');
      assert_equals(record.mediaType, null, 'mediaType');
      assert_equals(record.id, "dummy_record_id", 'id');
      const embedded_records = record.toRecords();
      assert_equals(embedded_records.length, 1, 'Only one embedded record.');
      // The only one embedded record has correct content.
      assert_equals(embedded_records[0].recordType, 'text', 'recordType');
      assert_equals(embedded_records[0].mediaType, null, 'mediaType');
      assert_equals(embedded_records[0].id, test_record_id, 'id');
      const decoder = new TextDecoder();
      assert_equals(decoder.decode(embedded_records[0].data), test_text_data,
          'data has the same content with the original dictionary');
    }
  }, 'NDEFRecord constructor with external record type');

  test(() => {
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord(':xyz', test_buffer_data)),
        'The local type record must be embedded in the payload of another record (smart-poster, external, or local)');

    // The following test cases use an external type record embedding our target local type record.

    const local_record= createRecord(':xyz', undefined /* data */, 'dummy_id_for_local_type');
    const payload_message = createMessage([local_record]);
    const external_record_embedding_local_record = createRecord('example.com:foo', payload_message);

    local_record.data = "A string is not a BufferSource or NDEFMessageInit";
    assert_throws_js(TypeError, () => new NDEFRecord(external_record_embedding_local_record),
        'Only BufferSource and NDEFMessageInit are allowed to be the record data.');

    let buffer = new ArrayBuffer(4);
    new Uint8Array(buffer).set([1, 2, 3, 4]);
    // Feed ArrayBuffer.
    {
      local_record.data = buffer;
      const record = new NDEFRecord(external_record_embedding_local_record);
      const embedded_records = record.toRecords();
      assert_equals(embedded_records.length, 1, 'Only the one embedded local record.');
      // The embedded local record is actually from |local_record|.
      assert_equals(embedded_records[0].recordType, ':xyz', 'recordType');
      assert_equals(embedded_records[0].mediaType, null, 'mediaType');
      assert_equals(embedded_records[0].id, 'dummy_id_for_local_type', 'id');
      assert_array_equals(new Uint8Array(embedded_records[0].data.buffer), [1, 2, 3, 4],
          'data has the same content with the original buffer');
      assert_equals(embedded_records[0].toRecords(), null,
          'toRecords() returns null if the payload is not an NDEF message.');
    }
    // Feed ArrayBufferView.
    {
      let buffer_view = new Uint8Array(buffer, 1);
      local_record.data = buffer_view;
      const record = new NDEFRecord(external_record_embedding_local_record);
      const embedded_records = record.toRecords();
      assert_equals(embedded_records.length, 1, 'Only the one embedded local record.');
      // The embedded local record is actually from |local_record|.
      assert_equals(embedded_records[0].recordType, ':xyz', 'recordType');
      assert_equals(embedded_records[0].mediaType, null, 'mediaType');
      assert_equals(embedded_records[0].id, 'dummy_id_for_local_type', 'id');
      assert_array_equals(new Uint8Array(embedded_records[0].data.buffer), [2, 3, 4],
          'data has the same content with the original buffer view');
      assert_equals(embedded_records[0].toRecords(), null,
          'toRecords() returns null if the payload is not an NDEF message.');
    }
    // Feed NDEFMessageInit.
    {
      const payload_message = createMessage([createTextRecord(test_text_data)]);
      local_record.data = payload_message;
      const record = new NDEFRecord(external_record_embedding_local_record);
      const embedded_records = record.toRecords();
      assert_equals(embedded_records.length, 1, 'Only the one embedded local record.');
      // The embedded local record is actually from |local_record|.
      assert_equals(embedded_records[0].recordType, ':xyz', 'recordType');
      assert_equals(embedded_records[0].mediaType, null, 'mediaType');
      assert_equals(embedded_records[0].id, 'dummy_id_for_local_type', 'id');
      // The embedded local record embeds another text record that's from |payload_message|.
      const embedded_records_in_local_record = embedded_records[0].toRecords();
      assert_equals(embedded_records_in_local_record.length, 1, 'Only one embedded record.');
      // The only one embedded record has correct content.
      assert_equals(embedded_records_in_local_record[0].recordType, 'text', 'recordType');
      assert_equals(embedded_records_in_local_record[0].mediaType, null, 'mediaType');
      assert_equals(embedded_records_in_local_record[0].id, test_record_id, 'id');
      const decoder = new TextDecoder();
      assert_equals(decoder.decode(embedded_records_in_local_record[0].data), test_text_data,
          'data has the same content with the original dictionary');
    }
  }, 'NDEFRecord constructor with local record type');

  test(() => {
    let buffer = new ArrayBuffer(4);
    new Uint8Array(buffer).set([1, 2, 3, 4]);
    const encoder = new TextEncoder();
    const uri_record = createUrlRecord(test_url_data);
    const title_record = createTextRecord(test_text_data, 'utf-8', 'en');
    const type_record = createRecord(':t', encoder.encode("image/gif"));
    const size_record = createRecord(':s', new Uint32Array([4096]));
    const action_record = createRecord(':act', new Uint8Array([3]));
    const icon_record = createRecord("mime", buffer, test_record_id, 'image/gif');
    const payload_message = createMessage([
        uri_record, title_record, type_record, size_record, action_record, icon_record]);
    const smart_poster_record = createRecord('smart-poster', payload_message, "dummy_record_id");

    const record = new NDEFRecord(smart_poster_record);
    assert_equals(record.recordType, 'smart-poster', 'recordType');
    assert_equals(record.mediaType, null, 'mediaType');
    assert_equals(record.id, 'dummy_record_id', 'id');
    const embedded_records = record.toRecords();
    assert_equals(embedded_records.length, 6, 'length');

    const decoder = new TextDecoder();
    let embedded_record_types = [];
    for(let record of embedded_records) {
      embedded_record_types.push(record.recordType);
      switch(record.recordType) {
        case 'url':
          assert_equals(record.mediaType, null, 'uri record\'s mediaType');
          assert_equals(record.id, test_record_id, 'uri record\'s id');
          assert_equals(decoder.decode(record.data), test_url_data, 'uri record\'s data');
          break;
        case 'text':
          assert_equals(record.mediaType, null, 'title record\'s mediaType');
          assert_equals(record.id, test_record_id, 'title record\'s id');
          assert_equals(record.encoding, 'utf-8', 'title record\'s encoding');
          assert_equals(record.lang, 'en', 'title record\'s lang');
          assert_equals(decoder.decode(record.data), test_text_data, 'title record\'s data');
          break;
        case ':t':
          assert_equals(record.mediaType, null, 'type record\'s mediaType');
          assert_equals(record.id, null, 'type record\'s id');
          assert_equals(decoder.decode(record.data), 'image/gif', 'type record\'s data');
          break;
        case ':s':
          assert_equals(record.mediaType, null, 'size record\'s mediaType');
          assert_equals(record.id, null, 'size record\'s id');
          assert_equals(record.data.byteLength, 4, 'byteLength of size record\'s data');
          assert_equals(new Uint32Array(record.data.buffer)[0], 4096, 'value of size record\'s data');
          break;
        case ':act':
          assert_equals(record.mediaType, null, 'action record\'s mediaType');
          assert_equals(record.id, null, 'action record\'s id');
          assert_equals(record.data.byteLength, 1, 'byteLength of action record\'s data');
          assert_equals(new Uint8Array(record.data.buffer)[0], 3, 'value of action record\'s data');
          break;
        case 'mime':
          assert_equals(record.mediaType, 'image/gif', 'icon record\'s mediaType');
          assert_equals(record.id, test_record_id, 'icon record\'s id');
          assert_array_equals(new Uint8Array(record.data.buffer), [1, 2, 3, 4],
              'icon record\'s mediaType');
          break;
        default:
          assert_unreached("Unknown recordType");
      }
    }
    assert_array_equals(embedded_record_types.sort(),
        [':act', ':s', ':t', 'mime', 'text', 'url'],
        'smart-poster record\'s contained record types');
  }, 'NDEFRecord constructor with smart-poster record type');

  test(() => {
    const uri_record = createUrlRecord(test_url_data);
    const smart_poster_record = createRecord(
        'smart-poster', createMessage([uri_record]), 'dummy_record_id');
    const record = new NDEFRecord(smart_poster_record);
    assert_equals(record.recordType, 'smart-poster', 'recordType');
    assert_equals(record.mediaType, null, 'mediaType');
    assert_equals(record.id, 'dummy_record_id', 'id');
    const embedded_records = record.toRecords();

    // smart-poster record only contains a uri record.
    assert_equals(embedded_records.length, 1, 'length');
    const decoder = new TextDecoder();
    assert_equals(embedded_records[0].recordType, 'url', 'uri record\'s recordType');
    assert_equals(embedded_records[0].mediaType, null, 'uri record\'s mediaType');
    assert_equals(embedded_records[0].id, test_record_id, 'uri record\'s id');
    assert_equals(decoder.decode(embedded_records[0].data), test_url_data, 'uri record\'s data');
  }, 'NDEFRecord constructor with smart-poster record type that contains only a mandatory uri record');

  test(() => {
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord('EMptY')),
        'Unknown record type.');
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord('TeXt', test_text_data)),
        'Unknown record type.');
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord('uRL', test_url_data)),
        'Unknown record type.');
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord('Mime', test_buffer_data)),
        'Unknown record type.');
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord('sMart-PosTER', test_url_data)),
        'Unknown record type.');
  }, 'NDEFRecord constructor with record type string being treated as case sensitive');

  test(() => {
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord(
        'example.com:hellö', test_buffer_data)),
        'The external type must be an ASCII string.');

    // Length of the external type is 255, OK.
    const record = new NDEFRecord(createRecord(
        [...Array(251)].map(_ => 'a').join('') + ':xyz', test_buffer_data));
    // Exceeding 255, Throws.
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord(
        [...Array(252)].map(_ => 'a').join('') + ':xyz', test_buffer_data)),
        'The external type should not be longer than 255.');

    assert_throws_js(TypeError, () => new NDEFRecord(createRecord(
        'xyz', test_buffer_data)), 'The external type must have a \':\'.');
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord(
        ':xyz', test_buffer_data)), 'The domain should not be empty.');
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord(
        'example.com:', test_buffer_data)), 'The type should not be empty.');
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord(
        'example.com:xyz[', test_buffer_data)), 'The type should not contain \'[\'.');
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord(
        'example.com:xyz~', test_buffer_data)), 'The type should not contain \'~\'.');
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord(
        'example.com:xyz/', test_buffer_data)), 'The type should not contain \'/\'.');
  }, 'NDEFRecord constructor with invalid external record type');

  test(() => {
    const encoder = new TextEncoder();
    const uri_record = createUrlRecord(test_url_data);
    const title_record = createTextRecord(test_text_data, 'utf-8', 'en');
    const type_record = createRecord(':t', encoder.encode("image/gif"));
    const size_record = createRecord(':s', new Uint32Array([4096]));
    const action_record = createRecord(':act', new Uint8Array([0]));
    const icon_record = createRecord(
        "mime", test_buffer_data, test_record_id, 'image/gif');

    const invalid_data_list = [
      {data: 'A string is not a NDEFMessageInit', message: 'A string is not allowed.'},
      {data: test_buffer_data, message: 'An ArrayBuffer is not allowed.'},
      {data: createMessage([title_record, type_record, size_record, action_record, icon_record]),
        message: 'Must contain a URI record.'},
      {data: createMessage([uri_record, title_record, type_record, size_record,
          action_record, icon_record, uri_record]),
        message: 'Must not contain more than one uri record.'},
      {data: createMessage([uri_record, title_record, type_record, size_record,
          action_record, icon_record, type_record]),
        message: 'Must not contain more than one type record.'},
      {data: createMessage([uri_record, title_record, type_record, size_record,
          action_record, icon_record, size_record]),
        message: 'Must not contain more than one size record.'},
      {data: createMessage([uri_record, title_record, type_record, size_record,
          action_record, icon_record, action_record]),
        message: 'Must not contain more than one action record.'},
      {data: createMessage([uri_record, title_record, type_record, action_record,
          icon_record, createRecord(":s", new Uint8Array([1]))]),
        message: 'Size record must have payload as 4-byte 32 bit unsigned integer.'},
      {data: createMessage([uri_record, title_record, type_record, size_record,
          icon_record, createRecord(":act", new Uint32Array([0]))]),
        message: 'Action record must have payload as 1-byte 8 bit unsigned integer.'}
    ];

    invalid_data_list.forEach(entry => {
      assert_throws_js(TypeError, () => new NDEFRecord(createRecord('smart-poster', entry.data)),
          entry.message);
    });
  }, "NDEFRecord constructor for smart-poster record with invalid embedded records.");

  test(() => {
    assert_throws_js(TypeError, () => new NDEFRecord(createRecord(':xyz', test_buffer_data)),
        'The local type record must be embedded in the payload of another record (smart-poster, external, or local)');

    // The following test cases use an external type record embedding our target local type record.

    const local_record= createRecord(':xyz', test_buffer_data);
    const payload_message = createMessage([local_record]);
    const external_record_embedding_local_record = createRecord('example.com:foo', payload_message);

    // OK.
    new NDEFRecord(external_record_embedding_local_record);
    local_record.recordType = ':xyZ123';
    new NDEFRecord(external_record_embedding_local_record);
    local_record.recordType = ':123XYz';
    new NDEFRecord(external_record_embedding_local_record);

    local_record.recordType = ':hellö';
    assert_throws_js(TypeError, () => new NDEFRecord(external_record_embedding_local_record),
        'The local type must be an ASCII string.');

    // Length of the local type excluding the prefix ':' is 255, OK.
    local_record.recordType = ':' + [...Array(255)].map(_ => 'a').join('');
    const record_255 = new NDEFRecord(external_record_embedding_local_record);

    // Exceeding 255, Throws.
    local_record.recordType = ':' + [...Array(256)].map(_ => 'a').join('');
    assert_throws_js(TypeError, () => new NDEFRecord(external_record_embedding_local_record),
        'The local type excluding the prefix \':\' should not be longer than 255.');

    local_record.recordType = 'xyz';
    assert_throws_js(TypeError, () => new NDEFRecord(external_record_embedding_local_record),
        'The local type must start with a \':\'.');

    local_record.recordType = ':Xyz';
    assert_throws_js(TypeError, () => new NDEFRecord(external_record_embedding_local_record),
        'The local type must have a lower case letter or digit following the prefix \':\'.');

    local_record.recordType = ':-xyz';
    assert_throws_js(TypeError, () => new NDEFRecord(external_record_embedding_local_record),
        'The local type must have a lower case letter or digit following the prefix \':\'.');
  }, 'NDEFRecord constructor with various local record types');

</script>
